#include "ChildrenCreationModule/ChildCreationRequestListener.h"
#include "DataStructures/FifoException.h"
#include "CommunicationModule/DlmComm.h"
#include "CommunicationModule/DlmInfoListener.h"
#include "LoggerModule/Logger.h"
#include "LockManagerModule/LockManager.h"
#include "LockManagerModule/TimeoutManager.h"
#include <iostream>
#include <pthread.h>
#include <cstdlib>
#include <csignal>
#include <cassert>

void* initListener(void* listener)
{
        sigset_t mask;
        sigemptyset(&mask);
        sigaddset(&mask, SIGCHLD);
        pthread_sigmask(SIG_UNBLOCK, &mask, NULL);

        ((ChildCreationRequestListener*)listener)->listen();

        return NULL;
}

void* initInfoListener(void* listener)
{
        ((DlmInfoListener*)listener)->listen();

        return NULL;
}

int main(int argc, char* argv[])
{
        int dlm_pipe[2];
        int info_pipe[2];

        pthread_t listenerThread;
        pthread_t infoListenerThread;

        DlmComm* comm;
        ChildCreationRequestListener* listener;
        DlmInfoListener *infoListener;
        LockManager *lockManager;

        /* Block signal used for handling timeouts */
        sigset_t mask;
        sigemptyset(&mask);
        sigaddset(&mask, TIMEOUT_SIG);
        sigaddset(&mask, SIGCHLD);
        pthread_sigmask(SIG_BLOCK, &mask, NULL);

        if(!Logger::getInstance().setLogFile("dlm.log"))
                Logger::getInstance().logDlm(Logger::ERRNO, "[MAIN] error setting log file");

        /* Runs DLM as a deamon, first argument 1 - don't change current directory, second argument 1 - don't redirect stdio */
        daemon(1, 1);

        if (pipe(dlm_pipe) < 0)
        {
                Logger::getInstance().logDlm(Logger::ERRNO, "[MAIN] dlm_pipe initialization failure.");

                return -1;
        }
        if (pipe(info_pipe) < 0)
        {
                Logger::getInstance().logDlm(Logger::ERRNO, "[MAIN] info_pipe initialization failure.");

                return -1;
        }

        comm = new DlmComm(dlm_pipe[0]);
        infoListener = new DlmInfoListener(info_pipe[0]);
        listener = new ChildCreationRequestListener(comm, dlm_pipe[1], info_pipe[1]);
        lockManager = new LockManager(comm);

        try
        {
                listener->initializeFifo();
        } catch (FifoException& exception)
        {
                Logger::getInstance().logDlm(Logger::ERROR, "[MAIN] FIFO initialization failure: %s", exception.what());

                return -1;
        }

        Logger::getInstance().logDlm(Logger::INFO, "[MAIN] Daemon started successful.");

        pthread_create(&listenerThread, NULL, initListener, (void*)listener);
        pthread_create(&infoListenerThread, NULL, initInfoListener, (void*)infoListener);

        lockManager->run();

        pthread_join(listenerThread, NULL);

        delete listener;
        delete infoListener;

        delete comm;
        delete lockManager;

        close(dlm_pipe[0]);
        close(dlm_pipe[1]);
        close(info_pipe[0]);
        close(info_pipe[1]);

        return 0;
}
